#include <retro.h>
#include "retro.creator.h"

using namespace Retro::Creator::Toolbox;
using namespace Retro::Creator::GUI;
using namespace SPLib::Geom;

#define LAYOUT_MARGIN 4

#define NUMERIC_EDIT_STYLE \
  WS_CHILD | WS_TABSTOP | ES_NUMBER | ES_LEFT | ES_AUTOHSCROLL | ES_WANTRETURN
#define NUMERIC_EDIT_EXSTYLE \
  WS_EX_LEFT | WS_EX_CLIENTEDGE | WS_EX_CONTEXTHELP

#define NUMERIC_UPDOWN_STYLE \
  WS_CHILD | UDS_ARROWKEYS | UDS_HOTTRACK | UDS_SETBUDDYINT
#define NUMERIC_UPDOWN_EXSTYLE \
  WS_EX_LEFT | WS_EX_LTRREADING

enum ColorPickerItem
{
  ColorPickerWheel = 0x0001,
  ColorPickerHue,
  ColorPickerSat,
  ColorPickerVal,
  ColorPickerAlpha,
  ColorPickerSliderValue,
  ColorPickerSliderAlpha,
};

ColorPicker::ColorPicker() :
  mFont(CreateFont(
  16, 0, 0, 0, FW_DONTCARE, FALSE, FALSE, FALSE, DEFAULT_CHARSET,
  OUT_OUTLINE_PRECIS, CLIP_DEFAULT_PRECIS, CLEARTYPE_QUALITY,
  VARIABLE_PITCH, _T("Arial"))
  ),
  mTextFont(GetDC(NULL), mFont)
{
  mLayout   = NULL;
  mNoNotify = FALSE;
}

VOID  ColorPicker::NumericItem::create(
  ColorPicker& iParent,
  INT     iID,
  PCTSTR  iText,
  INT     iMin,
  INT     iMax,
  INT     iIncrement
  )
{
  label.create(iParent);
  label.setText(iText);
  label.setFont(&iParent.mTextFont);

  edit.create(iParent.getHandle(), WC_EDIT, _T(""),
    NUMERIC_EDIT_STYLE, NUMERIC_EDIT_EXSTYLE);

  edit.setFont(iParent.mFont);
  edit.setID(iID);

  scroll.create(iParent.getHandle(), UPDOWN_CLASS, _T(""),
    NUMERIC_UPDOWN_STYLE, NUMERIC_UPDOWN_EXSTYLE);

  UDACCEL wIncrements[] =
  {
    { 0, iIncrement      },
    { 1, iIncrement * 2  },
    { 2, iIncrement * 10 }
  };

  scroll.send(UDM_SETRANGE32, iMin, iMax);
  scroll.send(UDM_SETBUDDY, (WPARAM)(HWND)edit, 0);
  scroll.send(UDM_SETACCEL, ARRAYSIZE(wIncrements), (LPARAM)wIncrements);
}

VOID  ColorPicker::NumericItem::addToLayout(
  LayoutElement* iLayout,
  INT iTextWidth,
  INT iEditWidth
  )
{
  iLayout->margins.setVert(LAYOUT_MARGIN);
  iLayout->addLeft(iTextWidth, label)->margins.left = LAYOUT_MARGIN;
  iLayout->addLeft(iEditWidth, edit);
  iLayout->addLeft(20, scroll);
}

VOID  ColorPicker::onCreate(Event::WndCreateEvent& iEvent)
{
  LayoutElement* wElement;

  mColorHue.create    (*this, ColorPickerHue,   _T("H"), 0, 360, 10);
  mColorSat.create    (*this, ColorPickerSat,   _T("S"), 0, 255, 5);
  mColorVal.create    (*this, ColorPickerVal,   _T("V"), 0, 255, 5);
  mColorAlpha.create  (*this, ColorPickerAlpha, _T("A"), 0, 255, 5);
  mValueSlider.create (*this);
  mAlphaSlider.create (*this);
  mColorWheel.create  (*this);

  mValueSlider.setID(ColorPickerSliderValue);
  mValueSlider.addStyle(WS_TABSTOP);
  mValueSlider.setMode(ColorSliderValue);

  mAlphaSlider.setID(ColorPickerSliderAlpha);
  mAlphaSlider.addStyle(WS_TABSTOP);
  mAlphaSlider.setMode(ColorSliderAlpha);

  mColorWheel.setID(ColorPickerWheel);
  mColorWheel.addStyle(WS_TABSTOP);
  mColorWheel.setWheelRadius(50);

  mLayout = getHandle();
  mLayout.margins.setVert(LAYOUT_MARGIN);

  // sprite color info
  wElement = mLayout.addLeft(80);
  mColorHue.addToLayout   (wElement->addTop(23), 20, 35);
  mColorSat.addToLayout   (wElement->addTop(23), 20, 35);
  mColorVal.addToLayout   (wElement->addTop(23), 20, 35);
  mColorAlpha.addToLayout (wElement->addTop(23), 20, 35);
  wElement->margins.right = LAYOUT_MARGIN * 3;

  // sprite color value
  wElement = mLayout.addLeft(22, 120, mValueSlider);
  wElement->flags = LAYOUT_ANCHOR_VCENTER;
  wElement->margins.right = LAYOUT_MARGIN;

  // sprite color alpha
  wElement = mLayout.addLeft(22, 120, mAlphaSlider);
  wElement->flags = LAYOUT_ANCHOR_VCENTER;

  // sprite color picker
  mLayout.addLeft(125, mColorWheel);
  mLayout.doLayout();
}

VOID  ColorPicker::onDestroy()
{
  mLayout.destroy();
  DeleteObject(mFont);
}

VOID  ColorPicker::onSize(Event::SizeEvent& iEvent)
{
  mLayout.doLayout();
}

VOID  ColorPicker::onCommand(Event::CommandEvent& iEvent)
{
  if (mNoNotify) return;

  switch (iEvent.id)
  {
  case ColorPickerHue:
  case ColorPickerSat:
    if (iEvent.code == EN_CHANGE)
    {
      HSVColor wHSV = mColorWheel.getColorHSV();

      wHSV.hue = (UINT)mColorHue.scroll.send(UDM_GETPOS32, NULL, NULL);
      wHSV.sat = (UINT)mColorSat.scroll.send(UDM_GETPOS32, NULL, NULL);

      RGBColor wRGB = wHSV;

      mNoNotify = TRUE;

      mColorWheel.setColor  (wRGB);
      mValueSlider.setColor (wRGB);
      mAlphaSlider.setColor (wRGB);
      updateColor();

      mNoNotify = FALSE;
    }
    break;
  case ColorPickerVal:
    if (iEvent.code == EN_CHANGE)
    {
      mNoNotify = TRUE;

      mValueSlider.setValue((LONG)
        mColorVal.scroll.send(UDM_GETPOS32, NULL, NULL));
      updateColor();

      mNoNotify = FALSE;
    }
    break;
  case ColorPickerAlpha:
    if (iEvent.code == EN_CHANGE)
    {
      mNoNotify = TRUE;

      mAlphaSlider.setValue((LONG)
        mColorAlpha.scroll.send(UDM_GETPOS32, NULL, NULL));
      updateColor();

      mNoNotify = FALSE;
    }
    break;
  }
}

VOID  ColorPicker::onNotify(Event::NotifyEvent& iEvent)
{
  if (mNoNotify) return;

  switch (iEvent.data->idFrom)
  {
  case ColorPickerWheel:
    updateColor();

    mValueSlider.setColor(mColorWheel.getColorRGB());
    mAlphaSlider.setColor(mColorWheel.getColorRGB());
    break;
  case ColorPickerSliderAlpha:
  case ColorPickerSliderValue:
    updateColor();
    break;
  }
}

VOID  ColorPicker::onFocus(Event::FocusEvent& iEvent)
{
  mColorHue.edit.focus();
}

VOID  ColorPicker::setColor(COLOR iValue)
{
  mColor = Geom::RGBColor::get(iValue);

  Geom::HSVColor wHSV = mColor;

  mNoNotify = TRUE;

  mValueSlider.setValue(wHSV.val);
  mAlphaSlider.setValue(wHSV.a);

  wHSV.val  = 255;
  wHSV.a    = 255;

  iValue = wHSV;

  mValueSlider.setColor (iValue);
  mAlphaSlider.setColor (iValue);

  mNoNotify = FALSE;

  mColorWheel.setColor  (iValue);

  sendNotify(ColorPickerNotifySelection);
}

VOID  ColorPicker::updateColor()
{
  Geom::HSVColor wHSV = mColorWheel.getColorHSV();

  wHSV.val  = mValueSlider.getValue();
  wHSV.a    = mAlphaSlider.getValue();

  if (!mNoNotify)
  {
    mNoNotify = TRUE;

    mColorHue.scroll.send   (UDM_SETPOS32, 0, wHSV.hue);
    mColorSat.scroll.send   (UDM_SETPOS32, 0, wHSV.sat);
    mColorVal.scroll.send   (UDM_SETPOS32, 0, wHSV.val);
    mColorAlpha.scroll.send (UDM_SETPOS32, 0, wHSV.a);

    mNoNotify = FALSE;
  }

  mColor = wHSV;

  sendNotify(ColorPickerNotifySelection);
}
